<!DOCTYPE html>





<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 3.9.0">
  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png?v=7.4.0">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png?v=7.4.0">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png?v=7.4.0">
  <link rel="mask-icon" href="/images/logo.svg?v=7.4.0" color="#222">

<link rel="stylesheet" href="/css/main.css?v=7.4.0">


<link rel="stylesheet" href="/lib/font-awesome/css/font-awesome.min.css?v=4.7.0">


<script id="hexo-configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/',
    scheme: 'Pisces',
    version: '7.4.0',
    exturl: false,
    sidebar: {"position":"left","display":"post","offset":12,"onmobile":false},
    copycode: {"enable":false,"show_result":false,"style":null},
    back2top: {"enable":true,"sidebar":false,"scrollpercent":false},
    bookmark: {"enable":false,"color":"#222","save":"auto"},
    fancybox: false,
    mediumzoom: false,
    lazyload: false,
    pangu: false,
    algolia: {
      appID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    },
    localsearch: {"enable":false,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},
    path: '',
    motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
    translation: {
      copy_button: '复制',
      copy_success: '复制成功',
      copy_failure: '复制失败'
    },
    sidebarPadding: 40
  };
</script>


<script data-ad-client="ca-pub-8048531516491146" async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>

  <meta name="description" content="一、非局部跳转1、简介大部分语言的运行控制模型，都是基于栈的。在这种模型中，调用一个函数时，就会将这个函数的参数、返回地址、局部变量等信息入栈；这个函数返回时，对应信息再出栈。正常情况下，函数调用的进栈和出栈都是成对出现的，比如函数的调用顺序是：func1 –&amp;gt; func2 – &amp;gt; func3，那么一定是 func1 先进栈，然后是 func2，最后是 func3；而 func3 调用">
<meta name="keywords" content="Objective-C, iOS,setjmp,longjmp,异常处理,try,catch">
<meta property="og:type" content="article">
<meta property="og:title" content="try catch 原理剖析">
<meta property="og:url" content="http://foogry.org/2018/11/15/2018-11-15-try-catch-yuan-li-pou-xi/index.html">
<meta property="og:site_name" content="foogry的技术博客">
<meta property="og:description" content="一、非局部跳转1、简介大部分语言的运行控制模型，都是基于栈的。在这种模型中，调用一个函数时，就会将这个函数的参数、返回地址、局部变量等信息入栈；这个函数返回时，对应信息再出栈。正常情况下，函数调用的进栈和出栈都是成对出现的，比如函数的调用顺序是：func1 –&amp;gt; func2 – &amp;gt; func3，那么一定是 func1 先进栈，然后是 func2，最后是 func3；而 func3 调用">
<meta property="og:locale" content="zh-CN">
<meta property="og:updated_time" content="2019-09-24T12:14:54.779Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="try catch 原理剖析">
<meta name="twitter:description" content="一、非局部跳转1、简介大部分语言的运行控制模型，都是基于栈的。在这种模型中，调用一个函数时，就会将这个函数的参数、返回地址、局部变量等信息入栈；这个函数返回时，对应信息再出栈。正常情况下，函数调用的进栈和出栈都是成对出现的，比如函数的调用顺序是：func1 –&amp;gt; func2 – &amp;gt; func3，那么一定是 func1 先进栈，然后是 func2，最后是 func3；而 func3 调用">
  <link rel="canonical" href="http://foogry.org/2018/11/15/2018-11-15-try-catch-yuan-li-pou-xi/">


<script id="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome: false,
    isPost: true,
    isPage: false,
    isArchive: false
  };
</script>

  <title>try catch 原理剖析 | foogry的技术博客</title>
  


  <script>
    var _hmt = _hmt || [];
    (function() {
      var hm = document.createElement("script");
      hm.src = "https://hm.baidu.com/hm.js?2944231de73b405a5da5c6b4097cfa0e";
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(hm, s);
    })();
  </script>






  <noscript>
  <style>
  .use-motion .brand,
  .use-motion .menu-item,
  .sidebar-inner,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header { opacity: initial; }

  .use-motion .logo,
  .use-motion .site-title,
  .use-motion .site-subtitle {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line-before i { left: initial; }
  .use-motion .logo-line-after i { right: initial; }
  </style>
</noscript>

</head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-CN">
  <div class="container use-motion">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-meta">

    <div>
      <a href="/" class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">foogry的技术博客</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
        <h1 class="site-subtitle" itemprop="description">Stay hungry,stay foolish.</h1>
      
  </div>

  <div class="site-nav-toggle">
    <button aria-label="切换导航栏">
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>


<nav class="site-nav">
  
  <ul id="menu" class="menu">
      
      
      
        
        <li class="menu-item menu-item-home">
      
    

    <a href="/" rel="section"><i class="menu-item-icon fa fa-fw fa-home"></i> <br>首页</a>

  </li>
      
      
      
        
        <li class="menu-item menu-item-archives">
      
    

    <a href="/archives/" rel="section"><i class="menu-item-icon fa fa-fw fa-archive"></i> <br>归档</a>

  </li>
      
      
      
        
        <li class="menu-item menu-item-about">
      
    

    <a href="/about/" rel="section"><i class="menu-item-icon fa fa-fw fa-user"></i> <br>关于</a>

  </li>
  </ul>

</nav>
</div>
    </header>

    
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>


    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
            

          <div id="content" class="content">
            

  <div id="posts" class="posts-expand">
      <article itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block post">
    <link itemprop="mainEntityOfPage" href="http://foogry.org/2018/11/15/2018-11-15-try-catch-yuan-li-pou-xi/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="foogry">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/images/avatar.gif">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="foogry的技术博客">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">try catch 原理剖析

          
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              
                
              

              <time title="创建时间：2018-11-15 08:27:01" itemprop="dateCreated datePublished" datetime="2018-11-15T08:27:01+08:00">2018-11-15</time>
            </span>
          
            

            
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="fa fa-calendar-check-o"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2019-09-24 20:14:54" itemprop="dateModified" datetime="2019-09-24T20:14:54+08:00">2019-09-24</time>
              </span>
            
          
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/categories/iOS/" itemprop="url" rel="index"><span itemprop="name">iOS</span></a></span>

                
                
              
            </span>
          

          
  
  <span class="post-meta-item">
    
      <span class="post-meta-item-icon">
        <i class="fa fa-comment-o"></i>
      </span>
        
      
      <span class="post-meta-item-text">Valine：</span>
    
    <a title="valine" href="/2018/11/15/2018-11-15-try-catch-yuan-li-pou-xi/#comments" itemprop="discussionUrl"><span class="post-comments-count valine-comment-count" data-xid="/2018/11/15/2018-11-15-try-catch-yuan-li-pou-xi/" itemprop="commentCount"></span></a>
  </span>
  
  

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
        <h2 id="一、非局部跳转"><a href="#一、非局部跳转" class="headerlink" title="一、非局部跳转"></a>一、非局部跳转</h2><h3 id="1、简介"><a href="#1、简介" class="headerlink" title="1、简介"></a>1、简介</h3><p>大部分语言的运行控制模型，都是基于栈的。在这种模型中，调用一个函数时，就会将这个函数的参数、返回地址、局部变量等信息入栈；这个函数返回时，对应信息再出栈。正常情况下，函数调用的进栈和出栈都是成对出现的，比如函数的调用顺序是：func1 –&gt; func2 – &gt; func3，那么一定是 func1 先进栈，然后是 func2，最后是 func3；而 func3 调用结束后一定是先返回到 func2 ，然后从 func2 返回到func3，而不能直接从 func3 返回到 func1。</p>
<p>我们都知道，C 语言中的 goto 语句，可以实现在一个函数内部跳转;与此同时，C 语言还提供了一种能够在函数间跳转、被称为 <code>非局部跳转</code> (no-local goto) 的机制，这种机制可以允许从一个多层嵌套的函数调用中直接返回。我们先通过下面的栗子来见证它的神奇之处：</p>
<a id="more"></a>

<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line">#include &lt;stdio.h&gt;</span><br><span class="line">#include &lt;setjmp.h&gt;</span><br><span class="line"></span><br><span class="line">jmp_buf jump_buffer;</span><br><span class="line"></span><br><span class="line">void func2(void)</span><br><span class="line">&#123;</span><br><span class="line">printf(&quot;Before calling longjmp\n&quot;);</span><br><span class="line">longjmp(jump_buffer, 1);</span><br><span class="line">printf(&quot;After calling longjmp\n&quot;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">void func1(void)</span><br><span class="line">&#123;</span><br><span class="line">printf(&quot;Before calling func2\n&quot;);</span><br><span class="line">func2();</span><br><span class="line">printf(&quot;After calling func2\n&quot;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">int main()</span><br><span class="line">&#123;</span><br><span class="line">if (setjmp(jump_buffer) == 0)&#123;</span><br><span class="line">printf(&quot;first calling set_jmp\n&quot;);</span><br><span class="line">func1();</span><br><span class="line">&#125; else &#123;</span><br><span class="line">printf(&quot;second calling set_jmp\n&quot;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">return 0;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>运行结果如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">first calling set_jmp</span><br><span class="line">Before calling func2</span><br><span class="line">Before calling longjmp</span><br><span class="line">second calling set_jmp</span><br></pre></td></tr></table></figure>

<p>从日志可以看出，函数的执行过程跳过了 <code>After calling func2</code>、<code>After calling longjmp</code> 两句日志所在的代码行，在 <code>func2</code> 中执行了 <code>longjmp</code> 方法后函数直接从 <code>func2</code> 跳转回了 <code>main</code> 函数中继续执行，而没经过 <code>func1</code>！</p>
<h3 id="2、实现机制"><a href="#2、实现机制" class="headerlink" title="2、实现机制"></a>2、实现机制</h3><p>非局部跳转功能主要是通过位于 <code>&lt;setjmp.h&gt;</code> 中的 <code>setjmp</code> 和 <code>longjmp</code> 两个函数实现。</p>
<ul>
<li>setjmp</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">`int setjmp(jmp_buf env);`</span><br></pre></td></tr></table></figure>

<p>可以把当前代码行的状态信息保存到 env 中，供以后 <code>longjmp</code> 恢复状态信息时使用。如果直接调用 <code>setjmp()</code>，则返回值为 0；如果是由于调用了 <code>longjmp</code> 而调用到 <code>setjmp</code>，则返回值为 <code>longjmp</code> 第二个参数所指定的值。</p>
<ul>
<li>longjmp</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">`void longjmp(jmp_buf env, int val);`</span><br></pre></td></tr></table></figure>

<p>用于将调用堆栈恢复成最近一次调用 <code>setjmp</code> 时所保存到 env 中的状态信息。也就是说，调用了 <code>longjmp</code> 后，不管当前调用堆栈在哪个方法中，都会回到有效范围内最近一次调用 <code>setjmp</code> 方法的地方，而 <code>setjmp</code> 方法的返回值就是这里设置的 <code>val</code> 的值，用于区分到底是从哪个 <code>longjmp</code> 返回到的 <code>setjmp</code>。</p>
<p>而 <code>jmp_buf</code> 是 <code>&lt;setjmp.h&gt;</code> 文件中定义的结构类型，用于保存系统状态信息。函数 <code>setjmp</code> 会将其所在的程序点的系统状态信息都保存到 <code>jmp_buf</code> 类型的结构变量 env 中，而调用 <code>longjmp</code> 会将 env 的系统状态信息恢复，以实现非局部跳转的功能。</p>
<h3 id="3、注意事项"><a href="#3、注意事项" class="headerlink" title="3、注意事项"></a>3、注意事项</h3><ul>
<li>执行顺序</li>
</ul>
<p><code>setjmp</code> 和 <code>longjmp</code> 结合使用时，必须要有严格的先后执行顺序，即先调用 <code>setjmp</code> 函数，再调用 <code>longjmp</code> 函数。否则如果在 <code>setjmp</code> 之前调用 <code>longjmp</code>，将导致程序的执行流变的不可预测，有可能导致程序崩溃。</p>
<ul>
<li>作用域</li>
</ul>
<p><code>longjmp</code> 必须在正确的 <code>setjmp</code> 的作用域范围内。具体来说，在一个函数中调用了 <code>setjmp</code>，只要该函数没有返回，那么在任何其它地方都可以通过 <code>longjmp</code> 调用来跳转到 <code>setjmp</code> 的下一条语句执行。</p>
<h2 id="二、try-catch-异常处理机制"><a href="#二、try-catch-异常处理机制" class="headerlink" title="二、try/catch 异常处理机制"></a>二、try/catch 异常处理机制</h2><p>在类 C 语言中，非局部跳转的一个重要应用场景就是 <code>异常处理机制</code>。Objective-C 使用 try/catch/finally 来捕获并处理异常，比如下面的代码：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">#import &lt;Foundation/Foundation.h&gt;</span><br><span class="line"></span><br><span class="line">int main (int argc, const char * argv[])</span><br><span class="line">&#123;</span><br><span class="line">@autoreleasepool</span><br><span class="line">&#123;</span><br><span class="line">@try &#123;</span><br><span class="line">NSException *e = [NSException</span><br><span class="line">exceptionWithName:@&quot;FileNotFoundException&quot;</span><br><span class="line">reason:@&quot;File Not Found on System&quot;</span><br><span class="line">userInfo:nil];</span><br><span class="line">@throw e;</span><br><span class="line">&#125;</span><br><span class="line">@catch (NSException *exception) &#123;</span><br><span class="line">if ([[exception name] isEqualToString:NSInvalidArgumentException]) &#123;</span><br><span class="line">NSLog(@&quot;%@&quot;, exception);</span><br><span class="line">&#125; else &#123;</span><br><span class="line">@throw exception;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line">@finally &#123;</span><br><span class="line">NSLog(@&quot;finally&quot;);</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line">return 0;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>通过 Clang 生成的 C 中间代码，可以看出 try/catch 的原理，上述代码保存成 main.m 文件后通过命令：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">clang -rewrite-objc main.m</span><br></pre></td></tr></table></figure>

<p>剔除无用信息后，可以得到下述代码：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br></pre></td><td class="code"><pre><span class="line">#include &lt;Foundation/Foundation.h&gt;</span><br><span class="line"></span><br><span class="line">int main (int argc, const char * argv[])</span><br><span class="line">&#123;</span><br><span class="line">@autoreleasepool</span><br><span class="line">&#123;</span><br><span class="line">/**</span><br><span class="line">* try/catch的作用域从这里开始</span><br><span class="line">*/</span><br><span class="line">/* @try scope begin */</span><br><span class="line">&#123;</span><br><span class="line">/**</span><br><span class="line">* 首先定义一个_objc_exception_data类型的结构体，用来保存异常现场的数据。</span><br><span class="line">*/</span><br><span class="line">struct _objc_exception_data</span><br><span class="line">&#123;</span><br><span class="line">/**</span><br><span class="line">* buf变量就是c语言中的jmp_buf</span><br><span class="line">* jmp_buf的定义可在setjmp.h文件中找到：</span><br><span class="line">*</span><br><span class="line">*      #define _JBLEN        (10 + 16 + 2)</span><br><span class="line">*      #define _JBLEN_MAX    _JBLEN</span><br><span class="line">*</span><br><span class="line">*      typedef int jmp_buf[_JBLEN];</span><br><span class="line">*/</span><br><span class="line">int buf[18/*32-bit i386*/];</span><br><span class="line"></span><br><span class="line">/**</span><br><span class="line">* pointers[0]用来存储通过@throw抛出的异常对象，</span><br><span class="line">* pointers[1]存储下一个_stack数据。</span><br><span class="line">*/</span><br><span class="line">char *pointers[4];</span><br><span class="line">&#125; _stack;</span><br><span class="line"></span><br><span class="line">/**</span><br><span class="line">* _rethrow保存可能在@catch中再次抛出的异常对象。</span><br><span class="line">*/</span><br><span class="line">id volatile _rethrow = 0;</span><br><span class="line"></span><br><span class="line">/**</span><br><span class="line">* 因为异常处理支持嵌套，_stack会被存储在一个全局的栈中，这个栈用单链表的存储结构表示。</span><br><span class="line">* objc_exception_try_enter函数将_stack压栈。</span><br><span class="line">*/</span><br><span class="line">objc_exception_try_enter(&amp;_stack);</span><br><span class="line"></span><br><span class="line">/**</span><br><span class="line">* _setjmp是C的函数，用于保存当前程序现场。</span><br><span class="line">* _setjmp需要传入一个jmp_buf参数，保存当前需要用到的寄存器的值。</span><br><span class="line">* _setjmp()它能返回两次，第一次是初始化时，返回0，第二次遇到_longjmp()函数调用会返回，返回值由_longjmp的第二个参数决定。</span><br><span class="line">* 如果对_setjmp()和_longjmp()概念不太了解的，请参考C语言的异常处理机制。</span><br><span class="line">*</span><br><span class="line">* 下面_setjmp()初始化返回0，然后执行if&#123;&#125;中也就是@try&#123;&#125;中的代码。</span><br><span class="line">*/</span><br><span class="line">if (!_setjmp(_stack.buf)) /* @try block continue */</span><br><span class="line">&#123;</span><br><span class="line">/**</span><br><span class="line">* 创建一个NSException对象，对应代码：</span><br><span class="line">*</span><br><span class="line">*             NSException *e = [NSException</span><br><span class="line">*                               exceptionWithName:@&quot;FileNotFoundException&quot;</span><br><span class="line">*                               reason:@&quot;File Not Found on System&quot;</span><br><span class="line">*                               userInfo:nil];</span><br><span class="line">*/</span><br><span class="line">NSException *e = ((NSException *(*)(id, SEL, NSString *, NSString *, NSDictionary *))(void *)objc_msgSend)(objc_getClass(&quot;NSException&quot;), sel_registerName(&quot;exceptionWithName:reason:userInfo:&quot;), (NSString *)&amp;__NSConstantStringImpl_main_m_0, (NSString *)&amp;__NSConstantStringImpl_main_m_1, (NSDictionary *)((void *)0));</span><br><span class="line"></span><br><span class="line">/**</span><br><span class="line">* 抛出异常对象，对应代码：@throw e;</span><br><span class="line">*</span><br><span class="line">* objc_exception_throw函数实现步骤如下：</span><br><span class="line">* 1. 把e对象保存到_stack-&gt;pointers[0]中使其在@catch&#123;&#125;中能被捕获。</span><br><span class="line">* 2. 将_stack从全局栈中弹出。</span><br><span class="line">* 3. 调用_longjmp()跳转到前面if语句中的_setjmp()位置。_longjmp()使得_setjmp()函数第二次返回，</span><br><span class="line">* 返回值为1，所以会执行else&#123;&#125;中也就是@catch&#123;&#125;中的代码。</span><br><span class="line">*/</span><br><span class="line">objc_exception_throw(e);</span><br><span class="line"></span><br><span class="line">&#125; /* @catch begin */ else &#123;</span><br><span class="line"></span><br><span class="line">/**</span><br><span class="line">* objc_exception_extract函数从_stack-&gt;pointers[0]中取得上面抛出的异常对象。</span><br><span class="line">*/</span><br><span class="line">id _caught = objc_exception_extract(&amp;_stack);</span><br><span class="line"></span><br><span class="line">/**</span><br><span class="line">* 这里为何再次调用objc_exception_try_enter对_stack压栈？先保留这个疑问，继续看下面的代码。</span><br><span class="line">*/</span><br><span class="line">objc_exception_try_enter (&amp;_stack);</span><br><span class="line"></span><br><span class="line">/**</span><br><span class="line">* 在@catch中设置一个跳转位置</span><br><span class="line">*/</span><br><span class="line">if (_setjmp(_stack.buf))</span><br><span class="line"></span><br><span class="line">/**</span><br><span class="line">* 如果@catch&#123;&#125;中再次抛出异常，在这里捕获。</span><br><span class="line">*/</span><br><span class="line">_rethrow = objc_exception_extract(&amp;_stack);</span><br><span class="line"></span><br><span class="line">else &#123; /* @catch continue */</span><br><span class="line"></span><br><span class="line">/**</span><br><span class="line">* objc_exception_match函数判断_caught对象是否是需要捕获的目标对象。对应代码：</span><br><span class="line">*</span><br><span class="line">* @catch (NSException *exception) &#123;</span><br><span class="line">*/</span><br><span class="line">if (objc_exception_match((struct objc_class *)objc_getClass(&quot;NSException&quot;), (struct objc_object *)_caught)) &#123;</span><br><span class="line">NSException *exception = _caught;</span><br><span class="line"></span><br><span class="line">/**</span><br><span class="line">* 比较捕获的异常是不是NSInvalidArgumentException类型。对应代码：</span><br><span class="line">*</span><br><span class="line">* if ([[exception name] isEqualToString:NSInvalidArgumentException]) &#123;</span><br><span class="line">*      NSLog(@&quot;%@&quot;, exception);</span><br><span class="line">*</span><br><span class="line">*/</span><br><span class="line">if (((BOOL (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)exception, sel_registerName(&quot;name&quot;)), sel_registerName(&quot;isEqualToString:&quot;), (NSString *)NSInvalidArgumentException)) &#123;</span><br><span class="line"></span><br><span class="line">NSLog((NSString *)&amp;__NSConstantStringImpl_main_m_2, exception);</span><br><span class="line">&#125; else &#123;</span><br><span class="line"></span><br><span class="line">/**</span><br><span class="line">* 抛出异常对象，然后跳转到前面@catch中的if语句中的_setjmp()位置。</span><br><span class="line">* 这就解释了前面为什么要在@catch中再次将_stack压栈和调用_setjmp()的原因。</span><br><span class="line">* 在当前@catch中，如果不设置一个跳转点来捕获@catch中抛出的异常，那么程序就直接跳转到全局栈的下一个@catch中，而下面的@finally&#123;&#125;代码就无法执行。</span><br><span class="line">* 在@catch中设置跳转点就是为了最后总能执行@finally中的代码。</span><br><span class="line">*/</span><br><span class="line">objc_exception_throw( exception);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">&#125; /* last catch end */ else &#123;</span><br><span class="line"></span><br><span class="line">/**</span><br><span class="line">* 如果异常对象没被处理，先将其保存到_rethrow变量。</span><br><span class="line">* objc_exception_try_exit函数将_stack从全局栈中弹出。</span><br><span class="line">*/</span><br><span class="line">_rethrow = _caught;</span><br><span class="line">objc_exception_try_exit(&amp;_stack);</span><br><span class="line">&#125;</span><br><span class="line">&#125; /* @catch end */</span><br><span class="line">&#125;</span><br><span class="line">/* @finally */</span><br><span class="line">&#123;</span><br><span class="line">if (!_rethrow) objc_exception_try_exit(&amp;_stack);</span><br><span class="line"></span><br><span class="line">NSLog((NSString *)&amp;__NSConstantStringImpl_main_m_3);</span><br><span class="line"></span><br><span class="line">/**</span><br><span class="line">* _rethrow是前面@catch中没有被处理的或被捕获的异常对象，</span><br><span class="line">* 最后，_rethrow异常对象被抛到全局栈的下一个@catch中。</span><br><span class="line">*/</span><br><span class="line">if (_rethrow) objc_exception_throw(_rethrow);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">&#125; /* @try scope end */</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line">return 0;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>以上代码还涉及了 objc_exception_try_enter、 objc_exception_extract、 objc_exception_throw、 objc_exception_try_exit 等函数，都可以在苹果开源 <a href="https://opensource.apple.com/source/objc4/" target="_blank" rel="noopener">objc4</a> 的 objc-exception.mm 文件中找到。</p>
<h2 id="参考文档"><a href="#参考文档" class="headerlink" title="参考文档"></a>参考文档</h2><ul>
<li><p><a href="https://www.cnblogs.com/lienhua34/archive/2012/04/22/2464859.html" target="_blank" rel="noopener">浅析C语言的非局部跳转：setjmp和longjmp</a></p>
</li>
<li><p><a href="https://blog.csdn.net/wykwdy007/article/details/6535322" target="_blank" rel="noopener">setjmp和longjmp函数使用详解</a></p>
</li>
<li><p><a href="https://www.cnblogs.com/markhy/p/3169035.html" target="_blank" rel="noopener">Objective-C try/catch异常处理机制原理</a></p>
</li>
</ul>

    </div>

    
    
    
        
      
        <div id="reward-container">
  <div></div>
  <button id="reward-button" disable="enable" onclick="var qr = document.getElementById(&quot;qr&quot;); qr.style.display = (qr.style.display === 'none') ? 'block' : 'none';">
    打赏
  </button>
  <div id="qr" style="display: none;">
        
      
      <div style="display: inline-block">
        <img src="/images/weixinpay.jpg" alt="foogry 微信支付">
        <p>微信支付</p>
      </div>

  </div>
</div>

      
        

<div>
<ul class="post-copyright">
  <li class="post-copyright-author">
    <strong>本文作者： </strong>foogry</li>
  <li class="post-copyright-link">
    <strong>本文链接：</strong>
    <a href="http://foogry.org/2018/11/15/2018-11-15-try-catch-yuan-li-pou-xi/" title="try catch 原理剖析">http://foogry.org/2018/11/15/2018-11-15-try-catch-yuan-li-pou-xi/</a>
  </li>
  <li class="post-copyright-license">
    <strong>版权声明： </strong>本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-nd/4.0/deed.zh" rel="noopener" target="_blank"><i class="fa fa-fw fa-creative-commons"></i>BY-NC-ND</a> 许可协议。转载请注明出处！</li>
</ul>
</div>

      

      <footer class="post-footer">
          
            
          
          <div class="post-tags">
            
              <a href="/tags/iOS/" rel="tag"># iOS</a>
            
              <a href="/tags/Objective-C/" rel="tag"># Objective-C</a>
            
              <a href="/tags/setjmp/" rel="tag"># setjmp</a>
            
              <a href="/tags/longjmp/" rel="tag"># longjmp</a>
            
              <a href="/tags/异常处理/" rel="tag"># 异常处理</a>
            
              <a href="/tags/try/" rel="tag"># try</a>
            
              <a href="/tags/catch/" rel="tag"># catch</a>
            
          </div>
        

        

          <div class="post-nav">
            <div class="post-nav-next post-nav-item">
              
                <a href="/2017/02/13/2017-02-13-iosnei-cun-tan-mi/" rel="next" title="iOS内存探秘">
                  <i class="fa fa-chevron-left"></i> iOS内存探秘
                </a>
              
            </div>

            <span class="post-nav-divider"></span>

            <div class="post-nav-prev post-nav-item">
              
                <a href="/2018/11/18/2018-11-18-bian-chang-can-shu-xue-xi-bi-ji/" rel="prev" title="变长参数学习笔记">
                  变长参数学习笔记 <i class="fa fa-chevron-right"></i>
                </a>
              
            </div>
          </div>
        
      </footer>
    
  </div>
  
  
  
  </article>

  </div>


          </div>
          
    
    <div class="comments" id="comments"></div>
  

        </div>
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside class="sidebar">
    <div class="sidebar-inner">
        
        
        
        
      

      <ul class="sidebar-nav motion-element">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <!--noindex-->
      <div class="post-toc-wrap sidebar-panel">
          <div class="post-toc motion-element"><ol class="nav"><li class="nav-item nav-level-2"><a class="nav-link" href="#一、非局部跳转"><span class="nav-number">1.</span> <span class="nav-text">一、非局部跳转</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#1、简介"><span class="nav-number">1.1.</span> <span class="nav-text">1、简介</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2、实现机制"><span class="nav-number">1.2.</span> <span class="nav-text">2、实现机制</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3、注意事项"><span class="nav-number">1.3.</span> <span class="nav-text">3、注意事项</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#二、try-catch-异常处理机制"><span class="nav-number">2.</span> <span class="nav-text">二、try/catch 异常处理机制</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#参考文档"><span class="nav-number">3.</span> <span class="nav-text">参考文档</span></a></li></ol></div>
        
      </div>
      <!--/noindex-->

      <div class="site-overview-wrap sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
  <p class="site-author-name" itemprop="name">foogry</p>
  <div class="site-description" itemprop="description"></div>
</div>
  <nav class="site-state motion-element">
      <div class="site-state-item site-state-posts">
        
          <a href="/archives/">
        
          <span class="site-state-item-count">22</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
    
      
      
      <div class="site-state-item site-state-categories">
        
        <span class="site-state-item-count">8</span>
        <span class="site-state-item-name">分类</span>
        
      </div>
    
      
      
      <div class="site-state-item site-state-tags">
        
        <span class="site-state-item-count">56</span>
        <span class="site-state-item-name">标签</span>
        
      </div>
    
  </nav>
  <div class="cc-license motion-element" itemprop="license">
    
  
    <a href="https://creativecommons.org/licenses/by-nc-nd/4.0/deed.zh" class="cc-opacity" rel="noopener" target="_blank"><img src="/images/cc-by-nc-nd.svg" alt="Creative Commons"></a>
  </div>



      </div>

    </div>
  </aside>
  <div id="sidebar-dimmer"></div>


      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright">&copy; <span itemprop="copyrightYear">2020</span>
  <span class="with-love" id="animate">
    <i class="fa fa-user"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">foogry</span>
</div>
  <div class="powered-by">Hosted by <a href="https://coding.net/pages" class="theme-link" rel="noopener" target="_blank">Coding Pages</a></div>
  <span class="post-meta-divider">|</span>
  <div class="powered-by">由 <a href="https://hexo.io" class="theme-link" rel="noopener" target="_blank">Hexo</a> 强力驱动 v3.9.0</div>
  <span class="post-meta-divider">|</span>
  <div class="theme-info">主题 – <a href="https://theme-next.org" class="theme-link" rel="noopener" target="_blank">NexT.Pisces</a> v7.4.0</div>

        












        
      </div>
    </footer>
  </div>

  


  <script src="/lib/anime.min.js?v=3.1.0"></script>
  <script src="/lib/velocity/velocity.min.js?v=1.2.1"></script>
  <script src="/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>
<script src="/js/utils.js?v=7.4.0"></script><script src="/js/motion.js?v=7.4.0"></script>
<script src="/js/schemes/pisces.js?v=7.4.0"></script>
<script src="/js/next-boot.js?v=7.4.0"></script>



  





















  

  

  


<script>
NexT.utils.getScript('//unpkg.com/valine/dist/Valine.min.js', () => {
  var GUEST = ['nick', 'mail', 'link'];
  var guest = 'nick,mail,link';
  guest = guest.split(',').filter(item => {
    return GUEST.includes(item);
  });
  new Valine({
    el: '#comments',
    verify: false,
    notify: false,
    appId: 'vqsvFaA8FXndr4867HJvtJ7P-gzGzoHsz',
    appKey: '6L6ddsrLCJVMetc2W7CL9kkf',
    placeholder: 'Comment Here',
    avatar: 'mm',
    meta: guest,
    pageSize: '10' || 10,
    visitor: false,
    lang: 'zh-cn' || 'zh-cn',
    path: location.pathname
  });
}, window.Valine);
</script>

</body>
</html>
